#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char b; //1个字节
typedef unsigned short w; //2个字节
typedef unsigned int dw; //4个字节


struct ListNode{
    char name[20];  //文件或目录的名称
    char total_name[60];    //文件或目录的全部名,不算.和..和EOF
    dw size;    //文件为正常大小，目录为0
    int start_pos;  //内容的起始位置，方便文件读取，.和..为0
    int type;   //文件夹0；文件1；终止-1
    int sub_pos;    //子目录的起始下标，txt或.或..为-1
};

int list_size = 0;  //数组大小

struct ListNode list[100];

struct RootEntry{
    char name[11];  //文件名8字节+拓展名3字节
    b Attr; //文件属性
    char reserved[10];  //保留位
    w WrtTime;  //最后一次写入时间
    w WrtDate;  //最后一次写入日期
    w FstClus;  //此条目录对应的开始簇号
    dw File_Size;   //文件大小
};


void List_Init();   //初始化链表
void Load(int parentNode, int begin, FILE *file);
int IdentifyEntry(char *name);  //返回0代表错误；返回1表示正确文件名/目录名；返回2代表.；返回3代表..
void Init_endNode();
void lsPrint(int pos);
void lslPrint(int pos);
void countDirAndFile(int sub_pos);
void my_print(char *c, int length);
void prepareOutputDir();
void prepareOutputFile();
void readFile(int pos, FILE *fat12);
int posLocation(char *p, int size);
void printNum(int num);

int main() {
    setbuf(stdout, NULL);
    char input[500];
    FILE *fat12 = fopen("a1.img","rb");
    List_Init();
    Load(0, 19*512, fat12);
    while (1){
        fgets(input, 500, stdin);
        if (input[0]=='\n')    //直接回车时不输出任何东西
            continue;
        int count = 0;  //直接输入空格时不输出任何东西
        for (int i = 0; i < strlen(input)-1; i++)
        {
            if (input[i]==' ')
            {
                count++;
            }
            else
            {
                break;
            }
        }
        if (count==strlen(input)-1)
        {
            continue;
        }
        
        
        input[strlen(input)-1] = 0x00;
        char command_type[20];
        int type = 0;//1是ls，2是ls -l，3是cat
        int input_pos =0;

        //0x00 null 0x20 space
        while(input[input_pos] != 0x20 && input[input_pos] != 0x00){
            command_type[input_pos] = input[input_pos];
            input_pos++;
        }
        if(command_type[0] == 'c' && command_type[1] == 'a' && command_type[2] == 't'){ //cat
            type =3;
        }
        else if(command_type[0] == 'l' && command_type[1] == 's'){  //ls
            type =1;
        }

        else if(input[0]=='e'&&input[1] == 'x' && input[2] == 'i'&& input[3] =='t'){    //exit
            break;
        }
        else{   //wrong command
            prepareOutputDir();
            char * warn = "Command Wrong!\n";
            my_print(warn,15);
            prepareOutputFile();
            continue;
        }
        char dir[50];
        int dir_num = 0;
        int flag = 1;//0代表异常
        int temp =0;
        //次while循环用于判断是否为ls-l，并获取cat或ls-l命令中的目录
        while(input[input_pos] != 0x00){
            //不是空格的时候
            if(input[input_pos] !=0x20){
                if(input[input_pos]=='-'){  //说明是ls -l
                    type = 2;
                    input_pos++;
                    while(input[input_pos] != 0x20 && input[input_pos] !=0x00){
                        if(input[input_pos] != 'l'){    //如果-后面没有l 说明command错误 flag置为0
                            flag = 0;
                            break;
                        }
                        input_pos++;
                    }

                }
                else{   //获取目录
                    if(dir_num >= 1){   //目录数>1 非法
                        flag = 0;
                    }
                    else{
                        dir_num++;
                        while(input[input_pos] != 0x20 && input[input_pos] !=0x00){
                            dir[temp] = input[input_pos];
                            input_pos++;
                            temp++;
                        }
                    }

                }
                if(flag == 0){
                    break;
                }
            }
            if(input[input_pos] ==0x00){    //读取到0x00时说明读完了 结束循环
                break;
            }
            input_pos++;
        }
        if(flag == 1 ){ //合法command
            char final_dir[50];
            final_dir[0] = '/';
            int start_pos = 0;
            //调整指针位置
            for(int i = 0;i<temp;i++){
                if(dir[i]!='.'&&dir[i]!='/'){
                    start_pos = i;
                    break;
                }
            }
            int pos_A = 1;  //最终目录长度

            if((temp == 1 &&dir[0] == '/')||(temp == 1 &&dir[0] == '.')||(temp == 2 &&dir[0] == '.'&&dir[1] == '/')){
                //骚操作 输入的目录为/或.或./时，默认为根目录
            }
            else {
                for (int i = start_pos; i < temp; i++) {    //dir给final_dir赋值
                    final_dir[pos_A] = dir[i];
                    pos_A++;
                }
            }
            if(type!=3){    //如果是ls -l命令，且有目录，则在用于寻找位置的final_dir的最后加上/
                if(temp>0&&dir[temp-1] != '/') {
                    final_dir[pos_A] = '/';
                    pos_A++;
                }
            }

            char *  p = final_dir;
            int f_pos = posLocation(p,pos_A);
            if(f_pos == -1){
                prepareOutputDir();
                char * warning = "File not found!\n";
                my_print(warning,16);
                prepareOutputFile();
            }
            else{
                if(type== 1){
                    lsPrint(f_pos);
                }
                else if(type == 2){
                    lslPrint(f_pos);
                }
                else if(type ==3){
                    int index = 0;
                    for (int i = pos_A-1; i >1 ; i--) {
                        if (final_dir[i]!='.')
                            continue;
                        else{
                            index = i;
                            break;
                        }
                    }
                    if (index!=0){
                        if (pos_A-index == 1){
                            char *warning = "It Is A Directory!\n";
                            prepareOutputDir();
                            my_print(warning, 19);
                            prepareOutputFile();
                        }
                        else if (pos_A-index-1 != 3){
                            char *warning ="Not A Standard TXT File!\n";
                            prepareOutputDir();
                            my_print(warning, 25);
                            prepareOutputFile();
                        }
                        else{
                            if (final_dir[index+1]=='T' && final_dir[index+2]=='X' && final_dir[index+3]=='T')
                                readFile(f_pos,fat12);
                            else{
                                char *warning ="Not A Standard TXT File!\n";
                                prepareOutputDir();
                                my_print(warning, 25);
                                prepareOutputFile();
                            }
                        }
                    }
                    else{
                        char *warning = "It Is A Directory!\n";
                        prepareOutputDir();
                        my_print(warning, 19);
                        prepareOutputFile();
                    }
                }
            }

        }
        else{
            prepareOutputDir();
            char * warning = "Wrong input!\n";
            my_print(warning,13);
            prepareOutputFile();
        }
	prepareOutputFile();
    }
    return 0;
}
//每次递归找子目录/文件结束后，添加一个endNode用于输出判断
void Init_endNode(){
    list[list_size].name[0] = '0';
    list[list_size].total_name[0] = '0';
    list[list_size].sub_pos = -1;
    list[list_size].start_pos = 0;
    list[list_size].size = -1;
    list[list_size].type = -1;
    list_size++;
}
//初始化root目录
void List_Init(){
    list[list_size].name[0] = '/';
    list[list_size].total_name[0] = '/';
    list[list_size].sub_pos = 2;
    list[list_size].start_pos = 33*512; //19+14
    list[list_size].size = -1;
    list[list_size].type = 0;
    list_size++;
    Init_endNode();
}

/**
 *
 * @param parentNode 父节点
 * @param begin 读取的起始位置
 * @param fat12 img镜像
 */
void Load(int parentNode, int begin, FILE *fat12){
    int temp_size = list_size;
    list[parentNode].sub_pos = list_size;   //parentnode的sub_pos指向第一个子目录
    struct RootEntry entry0;
    struct RootEntry *entryPtr = &entry0;
    int max = 224;  //根目录最多有224个(包括文件)   14*512/32
    if (parentNode!=0)
    {
        max = 16;   //子目录最多16个    512/32
    }
    
    for (int i = 0; i < max; i++) {
        fseek(fat12, begin+i*32, 0);
        fread(entryPtr, 1, 32, fat12);
        //windows下制作的软盘x00是文件，linux里是x20，文件夹都是x10，但linux里会有长文件问题，不知道img来源故这里只过滤linux中的长文件
        if (entryPtr->Attr==0x20 || entryPtr->Attr==0x10 || entryPtr->Attr==0x00){
            int flag = IdentifyEntry(entryPtr->name);
            //文件名有问题的情况
            if (flag==0){
                continue;
            }
            //flag为2或3的时候，说明为./..
            if (flag==2 || flag==3){
                list[list_size].name[0]='.';
                list[list_size].total_name[0] = '.';
                if (flag==3){
                    list[list_size].name[1]='.';
                    list[list_size].total_name[1] = '.';
                }
                list[list_size].type = 0;
                list[list_size].sub_pos = -1;
                list[list_size].size = -1;
                list[list_size].start_pos = 0;
            }
            else{   //flag=1 也就是说name为目录/文件
                list[list_size].type = (entryPtr->Attr==0x10) ? 0 : 1;  //0x10表示文件夹 type为0 否则，type为1，也就是文件
                list[list_size].size = (entryPtr->Attr==0x10) ? 0:entryPtr->File_Size;  //0x10表示文件夹，如果是文件夹，size设为0；否则，该名字为文件名，size设为文件大小
                list[list_size].start_pos = (entryPtr->FstClus+31)*512; //文件也是要递归的，起始簇号为2
                int endPos = 8; //endPos为8，因为文件/目录名最长为8
                for (int j = 0; j < 8; j++) {
                    //找到文件/目录名长度，并同时将文件/目录名存到list中
                    if (entryPtr->name[j]==0x20){
                        endPos=j;
                        break;
                    }
                    list[list_size].name[j]= entryPtr->name[j];
                }
                if (entryPtr->Attr!=0x10){  //0x10表示文件夹，如果不是文件夹，说明是文件，在name中添加后缀名.TXT
                    list[list_size].name[endPos]='.';
                    list[list_size].name[endPos+1]='T';
                    list[list_size].name[endPos+2]='X';
                    list[list_size].name[endPos+3]='T';
                    list[list_size].name[endPos+4]=0x00;
                }
                int pos = 0;
                //当前node的total_name为parentnode的total_name+当前node的name
                //当前node的total_name添加parentnode的total_name
                while (list[parentNode].total_name[pos]!=0x00){
                    list[list_size].total_name[pos] = list[parentNode].total_name[pos];
                    pos++;
                }
                int j_pos = 0;
                //当前node的total_name添加当前node的name
                while (list[list_size].name[j_pos]!=0x00){
                    list[list_size].total_name[pos] = list[list_size].name[j_pos];
                    pos++;
                    j_pos++;
                }
                //如果当前node为文件夹(目录)，则在total_name中添加/
                if (entryPtr->Attr==0x10){
                    list[list_size].total_name[pos] = '/';
                    pos++;
                }
                //最后在total_name添加终止
                list[list_size].total_name[pos] = 0x00;
            }
            list_size++;
        }
    }
    Init_endNode(); //每次递归结束，说明parentnode的一级子目录和文件已经找完，添加一个结束的node用于输出判断
    int temp_end = list_size;   //从当前node开始，到最后一个node结束 递归寻找子目录和文件
    for (int i = temp_size; i < temp_end; i++) {    //递归地从每个节点开始寻找子目录和文件    如果是目录，从当前目录起始位置开始找子目录和文件，文件/./..则不需要，递归寻找时，每次将当前node作为子目录/文件的parentnode
        if ((list[i].type==0) && (list[i].start_pos!=0)){
            Load(i, list[i].start_pos, fat12);
        }
    }
}

int IdentifyEntry(char *name){
    int flag = 1;
    if (name[0]==0x2E){
        flag = 2;   //.
        if (name[1]==0x2E){
            flag = 3;   //..
        }
        for (int i = 2; i < 11; i++) {
            if (name[i]!=0x20){ //如果.或者..后面的字符串不是0x20(space)则非法
                return 0;
            }
        }
        return flag;
    }
    //判断目录或者文件名是否合法
    for (int i = 0; i < 11; i++) {
        if (!((name[i]>='0' && name[i]<='9')
              || (name[i]>='a' && name[i]<='z')
              || (name[i]>='A' && name[i]<='Z')
              || (name[i]==' ')
              || (name[i]=='_'))){
            return 0;
        }
    }
    return flag;
}

void lsPrint(int pos){
    //prepareOutputDir();
    prepareOutputFile();
    char *c = list[pos].total_name;
    int len = 60;
    my_print(c,len);
    char *t = ":\n";
    my_print(t, 2);

    int sub_node = list[pos].sub_pos;
    int begin_pos = sub_node;   //从第一个子目录开始
    while (list[sub_node].type!=-1){    //type=-1说明是endNode 输出结束
        if (list[sub_node].type==1){
            prepareOutputFile();
            char *f_name = list[sub_node].name;
            my_print(f_name, 20);
        } else if (list[sub_node].type==0){
            prepareOutputDir();
            char *f_name = list[sub_node].name;
            my_print(f_name, 20);
        }
        if (list[begin_pos+1].type!=-1){
            char *x=" ";
            my_print(x, 1);
        }
        sub_node++;
    }
    char *x = "\n";
    my_print(x, 2);
    for (int i = begin_pos; i < sub_node; i++) {    //从第一个子目录开始，递归的输出子目录中的目录和文件
        if (list[i].type==0 && list[i].sub_pos!=-1){    //目录需要递归，文件/./..不需要
            lsPrint(i);
        }
    }
}

void lslPrint(int pos){
    //开始文件名
    prepareOutputFile();
    my_print(list[pos].total_name,60);
    //输出个数
    countDirAndFile(list[pos].sub_pos);
    char * p = ":\n";
    my_print(p,2);
    int sub_pos = list[pos].sub_pos;
    int begin = sub_pos;
    //到终止节点结束
    while(list[sub_pos].type!=-1){
        if(list[sub_pos].type == 1){    //文件的情况
            prepareOutputFile();
            my_print(list[sub_pos].name,20);
            char * blank = " ";
            my_print(blank,1);
            char prep[10];
            printNum(list[sub_pos].size);
            char * newline = "\n";
            my_print(newline,1);
        }
        else{   //目录的情况
            prepareOutputDir();
            my_print(list[sub_pos].name,20);
            //正常目录情况
            if(list[sub_pos].sub_pos!=-1){
                countDirAndFile(list[sub_pos].sub_pos);
            }
            char * newline = "\n";
            my_print(newline,1);
        }
        sub_pos++;
    }
    char * newline = "\n";
    my_print(newline,1);
    for(int i =begin;i<sub_pos;i++){    //从第一个子目录开始，递归的输出子目录中的目录和文件
        if( list[i].type ==0 && list[i].sub_pos!=-1){   //目录需要递归，文件/./..不需要
            lslPrint(i);
        }
    }
}

void countDirAndFile(int sub_pos){
    int dir_num = 0;
    int file_num = 0;
    while(list[sub_pos].type!=-1){  //到endNode结束
        //排除./..
        if(list[sub_pos].type == 0 &&(list[sub_pos].sub_pos!= -1)){dir_num++;}
        if(list[sub_pos].type == 1){file_num++;}
        sub_pos++;
    }
    prepareOutputFile();
    char * temp_p = " ";
    my_print(temp_p,1);
    printNum(dir_num);
    my_print(temp_p,1);
    printNum(file_num);
}

void readFile(int pos,FILE * fat12){
    int begin = list[pos].start_pos;
    char temp[512];
    char * begin_ptr = temp;
    fseek(fat12, begin, 0);
    fread(begin_ptr, 1, 512, fat12);
    char * temp_ptr = temp;

    prepareOutputFile();
    my_print(temp_ptr,512);
}

int posLocation(char * p,int size){
    char * p2;
    p2 = p;
    int flag = -1;//未找到
    for(int i = 0; i <list_size;i++){   //遍历所有节点
        flag = i;   //flag为所要找的pos
        p = p2;
        int len_total_name = 0; //用于判断total_name和size是否相等，若不相等，就不需要比较了，也为了排除类似于文件夹名为NJU和NJUSE造成的输出问题(若NJUSE在NJU前面，但想找的是NJU，若不比较长度，则会输出NJUSE中的内容)
        for (int k = 0; k < 60; k++) {
            if (list[i].total_name[k]!=0x00)
                len_total_name++;
            else
                break;
        }
        if (len_total_name!=size){
	    flag = -1;
	    continue;
		
        }
        for(int j = 0; j <size; j++){   //每个节点的name与p比较 若完全相同，则说明位置找到，有一个地方不一样就结束循环，从下一个节点开始比较
            if(*p == list[i].total_name[j]){
                p++;
            }
            else{
                flag = -1;
                break;
            }
        }
        if(flag != -1){
            return flag;
        }
    }
    return flag;
}

void printNum(int num){
    char* number = (char*)malloc(5);
    //int num 转char * num
    int size = 1;
    for(int temp = num;temp>9;temp/=10){
        size++;
    }
    for(int i = size-1;i>=0;i--){
        number[i] = num%10+'0';
        num/=10;
    }
    my_print(number,10);
    free(number);
}
